用栈和递归解决迷宫问题

今天讲的是一个关于迷宫的面试题,首先咱们先看一下他的简单版本,模型如下图:


利用递归把问题简单化,找入口到出口的问题可以转化为从入口四周的合法的点到出口的问题,一旦找到就结束整个递归,在找的同时,把能走的路压到一个栈里面,然后探测四周,如果四周都不能走就退栈,如果某一个方向能走,进行压栈,然后转尔探测新的坐标的四周,代码如下:

#include<iostream>
#include<stack>
using namespace std;
struct Set
{
	Set(int x, int y) :_x(x), _y(y)
	{}
	int _x;
	int _y;
};
//找一条路

template<int MAX>
class Mez
{
public:
	//初始化迷宫
	Mez(int arr[][MAX])
	{
		maparr = new int[MAX][MAX];
		for (int i = 0; i < MAX; i++)
		{
			for (int j = 0; j < MAX; j++)
			{
				maparr[i][j] = arr[i][j];
			}
		}
	}
	~Mez()
	{
		delete[] maparr;
	}
	void display()
	{
		for (int i = 0; i < MAX; i++)
		{
			for (int j = 0; j < MAX; j++)
			{
				printf("%4d", maparr[i][j]);
			}
			cout << endl << endl;
		}
		cout << endl;
	}
	bool  movement(Set&set, stack<Set>& Route)
	{
		if (Islegalset(set))
		{
			maparr[set._x][set._y] = 2;
			Route.push(set);
			if (Iswill(set))
			{
				return true;
			}
			//如果他的下一个位置为0则可以走。
			//朝上走
			if (maparr[set._x - 1][set._y] == 0)
			{
				if(movement(Set(set._x - 1, set._y), Route))
					return true;
			}
			//朝左走
			if (maparr[set._x][set._y - 1] == 0)
			{
				if(movement(Set(set._x, set._y - 1), Route))
					return true;
			}
			//朝右走
			if (maparr[set._x][set._y + 1] == 0)
			{
				if(movement(Set(set._x, set._y + 1), Route))
					return true;
			}
			//朝下走
			if (maparr[set._x + 1][set._y] == 0)
			{
				if (movement(Set(set._x + 1, set._y), Route))
					return true;
			}
			//如果周围的路都不通,把这个坐标pop出来,交给他的上一级调用者,继续探测周围。
			Route.pop();
			//把不通的路标记为3(也可以不标记)
			maparr[set._x][set._y] = 3;
		}
		return false;
	}
	bool Islegalset(const Set&set)
	{
		if (0 <= set._x&&set._x < MAX && 0 <= set._y&&set._y < MAX)
		{
			return true;
		}
		return false;
	}
	bool Iswill(const Set& set)
	{
		if (set._x == 0 || set._y == 0 || set._y == MAX - 1)
		{
			cout << "出口为:" << set._x << " " << set._y << endl;
			return true;
		}
		return false;
	}
private:
	int(*maparr)[MAX];
};

int main()
{
	int arr[11][11] = {
		{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 }
	};
	Mez<11> mez(arr);
	mez.display();
	//保存走的通路
	stack<Set>Route;
	mez.movement(Set(10, 6), Route);
	mez.display();
	return 0;
}

运行结果如下图:


上面只是一种简单形式的迷宫,假如迷宫有多条出路,要求走完所有出路,如图:

此时我们就不能在找到一条路时立马结束整个递归了程序,而是要遍历完四周所有能走的路时才可以结束程序,代码如下:

#include<iostream>
#include<stack>
using namespace std;
struct Set
{
	Set(int x, int y) :_x(x), _y(y)
	{}
	int _x;
	int _y;
};
template<int MAX>
class Mez
{
public:
	//初始化迷宫
	Mez(int arr[][MAX])
	{
		maparr = new int[MAX][MAX];
		for (int i = 0; i < MAX; i++)
		{
			for (int j = 0; j < MAX; j++)
			{
				maparr[i][j] = arr[i][j];
			}
		}
	}
	~Mez()
	{
		delete[] maparr;
	}
	void display()
	{
		for (int i = 0; i < MAX; i++)
		{
			for (int j = 0; j < MAX; j++)
			{
				printf("%4d", maparr[i][j]);
			}
			cout << endl << endl;
		}
		cout << endl;
	}
	void  movement(Set&set, stack<Set>& Route, stack<stack<Set>>&stackRoute)
	{

		if (Islegalset(set))
		{
			maparr[set._x][set._y] = 2;
			
			if (maparr[set._x][set._y] == 1)
			{
				return;
			}
			Route.push(set);
			if (Iswill(set))
			{
				//把所有的通路保存起来
				stackRoute.push(Route);
				//如果找到一条通路,在处理完之后把最后一个元素pop掉,把递归返回到他的上一级调用者,继续探测
				Route.pop();
				return;
			}
			//如果他的下一个位置为0则可以走。
			//朝上走
			if (maparr[set._x - 1][set._y] == 0 )
			{
				movement(Set(set._x - 1, set._y), Route, stackRoute);
			}
			//朝左走
			if (maparr[set._x][set._y - 1] == 0)
			{
				movement(Set(set._x, set._y - 1), Route, stackRoute);
			}
			//朝右走
			if (maparr[set._x][set._y + 1] == 0 )
			{
				movement(Set(set._x, set._y + 1), Route, stackRoute);
			}
			//朝下走
			if (maparr[set._x + 1][set._y] == 0 )
			{
				movement(Set(set._x + 1, set._y), Route, stackRoute);
			}
			//如果所以的路都不通,把这个坐标pop出来,交给他的上一级调用者,继续探测周围。
			Route.pop();
		}
	}
	bool Islegalset(const Set&set)
	{
		if (0 <= set._x&&set._x < MAX && 0 <= set._y&&set._y < MAX)
		{
			return true;
		}
		return false;
	}
	bool Iswill(const Set& set)
	{
		if (set._x == 0 || set._y == 0 || set._y == MAX - 1)
		{
			cout << "出口为:" << set._x << " " << set._y << endl;
			return true;
		}
		return false;
	}
private:
	int(*maparr)[MAX];
};

int main()
{
	int arr[11][11] = {
		{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
		{ 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 },
		{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 }
	};
	Mez<11> mez(arr);
	mez.display();
	//保存所有的通路
	stack<stack<Set>>ArrayRoute;
	//每次走的通路
	stack<Set>Route;
	mez.movement(Set(10, 6), Route,ArrayRoute);
	mez.display();
	printf("总共有%d条通路\n", ArrayRoute.size());
	return 0;
}

运行结果如下图:

如图所示,我们创建了一个栈专门保存所有的通路,而且也遍历完了所有的通路,但是这个也有一个问题,当里面存在环的时候,让你求迷宫里面的最短路问题,这样的标记就会出现错误,如下图:

运行结果如图:

以上就是关于迷宫的三个版本,如果有不对的地方,还请多多指教。。







  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您解答这个问题。 首先,我们需要定义一个结构体来表示迷宫中的一个点,包含其行列坐标以及是否被访问过的状态: ```c typedef struct { int x; int y; int visited; } Point; ``` 接下来,我们需要定义一个数据结构,用于存储走过的径。由于迷宫是二维的,我们可以用一个一维数组来模拟,每个元素表示一个点。定义的代码如下: ```c #define MAX_STACK_SIZE 100 typedef struct { Point data[MAX_STACK_SIZE]; int top; } Stack; ``` 然后,我们需要实现几个的基本操作函数,如初始化、判断是否为空、获取顶元素、入和出等: ```c void init(Stack *s) { s->top = -1; } int is_empty(Stack *s) { return s->top == -1; } Point peek(Stack *s) { return s->data[s->top]; } void push(Stack *s, Point p) { s->top++; s->data[s->top] = p; } Point pop(Stack *s) { Point p = s->data[s->top]; s->top--; return p; } ``` 现在,我们可以开始用栈解决迷宫问题了。我们可以用一个二维数组来表示迷宫地图,其中0表示通,1表示障碍物。假设起点为(0, 0),终点为(n-1, n-1),我们可以使用深度优先搜索算法来遍历迷宫,同时使用栈来记录径。搜索的过程中,每当走到一个新的可行的点时,将其加入中,并标记为已访问。如果当前点是终点,则输出径,否则继续递归搜索。如果当前点无法继续前进,则将其从中弹出,回溯到上一个点,继续搜索。代码如下: ```c void maze(int maze[][MAX_SIZE], int n) { Stack s; init(&s); Point start = {0, 0, 1}; push(&s, start); while(!is_empty(&s)) { Point cur = peek(&s); if(cur.x == n-1 && cur.y == n-1) { // 到达终点,输出径 for(int i = 0; i <= s.top; i++) { printf("(%d,%d) ", s.data[i].x, s.data[i].y); } printf("\n"); return; } int x = cur.x, y = cur.y; // 上 if(x > 0 && !maze[x-1][y]) { Point next = {x-1, y, 1}; maze[x-1][y] = 1; push(&s, next); continue; } // 右 if(y < n-1 && !maze[x][y+1]) { Point next = {x, y+1, 1}; maze[x][y+1] = 1; push(&s, next); continue; } // 下 if(x < n-1 && !maze[x+1][y]) { Point next = {x+1, y, 1}; maze[x+1][y] = 1; push(&s, next); continue; } // 左 if(y > 0 && !maze[x][y-1]) { Point next = {x, y-1, 1}; maze[x][y-1] = 1; push(&s, next); continue; } // 无法继续前进,回溯 pop(&s); } printf("No Solution!\n"); } ``` 这样,我们就用栈成功地解决迷宫问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值